function animatePipes() {
    var errorCount = 0;
    var loopingInterval;
    var pipeConditions, pipes = {};
    var advisedId = emosWS.getNewElementId();
    var tagList = [];
    var timeOutReached = true;  // This is that checkPipes() is only executed once per OPC update and not several times per second
    var debounceTime = 250; // Debounce of the checkPipes() function in ms
    var iFrameAdress = window.location.pathname;
    var iFrameAdressSplit = iFrameAdress.split('/');
    var processSchemePath = sessionStorage.getItem("apt_currentProccesSchemePath"); // Default animation path is the last selected process scheme, but overwrite with following function for cleaner device
    var processSystem = '';

    for (var i = 0; i < iFrameAdressSplit.length; i++) {
        if (iFrameAdressSplit[i] === "processSchemes") {    // Get the part of the adress after the processSchemes/...
            processSchemePath = iFrameAdressSplit[i + 1];
            processSystem = iFrameAdressSplit[i + 2];
            processSystem = processSystem.split('.')[0];
            break;
        }
    }

    svgContainer = document.getElementById("mainsvg").contentDocument;

    var pipeList = {};  // Tracks all pipes if they are animated or not. If not, the standard color is set
    var defaultColor;

    function initPipe() {
        // Load config json
        var PIPEANIMATIONCONFIG = '../' + processSchemePath + '/' + processSystem + '.json' + '?id=' + new Date().getTime();

        // Load Config
        $.ajax({
            dataType: "json",
            url: PIPEANIMATIONCONFIG,
            async: false,
            success: function (data) {
                populatePipeList(data);
                pipeConditions = data.pipes;
                defaultColor = data.defaultColor || "rgba(145,187,207,1)";  // Set to initial styleguid color if nothing specified;
                startAnimation();
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.error("Failed to load " + PIPEANIMATIONCONFIG + " Error: " + errorThrown + " textStatus: " + textStatus);
            }
        });

    }

    // This function scans the whole JSON for pipe ids and stores them in the pipeList
    function populatePipeList(configJson) {
        for (var rule in configJson.pipes) {
            if (configJson.pipes.hasOwnProperty(rule)) {
                configJson.pipes[rule].pipes.forEach(function (pipe) {
                    pipeList[pipe] = false;
                });
            }
        }
    }

    // This function resets the pipeList in order to notice, which pipes were animated and which not
    // Function is called for each animation cycle
    function clearPipeList() {
        for (var key in pipeList) {
            if (pipeList.hasOwnProperty(key)) {
                pipeList[key] = false;
            }
        }
    }

    // Checks the pipeList and resets all not animated pipes to the default color
    function checkPipeList() {
        for (var key in pipeList) {
            if (pipeList.hasOwnProperty(key)) {
                if (pipeList[key] === false) {
                    setPipeColor(key, defaultColor);
                }
            }
        }
    }

    function startAnimation() {
        // Initially check the pipes and advise all necessary tags. After that all updates are handled by adviseNewTag() function.
        checkPipes();

        /*
        Cleanup all advised tags after navigating to another window.

        ToDo: When switching slides in the powerpoint, the algorithm doesn't notice
        that the window changed.The animation will still try to access the pipes
        and cannot find them, thus creating errors.
        */
        $('#plants').on("remove", function () {
            console.log("Window is removed");
            emosWS.unadvise(advisedId);
        });
        window.onbeforeunload = function () {
            console.log("Window is reloaded");
            emosWS.unadvise(advisedId);
        };
    }

    /*
        This function evaluates all conditions from the Configuration File. 
    */
    function checkPipes() {
        clearPipeList();
        for (var ruleName in pipeConditions) {
            var pipe = pipeConditions[ruleName];
            var pipeAnimated = false;
            pipe.conditions.forEach(function (condition) {
                if (typeof condition === "string") return; // Filter away the "OR" statements in array

                // AND-Block means all conditions must be true. 
                var numberConditionsTrue = 0;
                var numberConditionsTotal = 0;

                condition.forEach(function (expression) {
                    if (typeof expression === "string") return; // Filter away the "AND" statements in array

                    numberConditionsTotal++;
                    var currentValue = getElementState(expression);
                    // Negate this rule? true means false and false means true
                    if (expression.negation) {
                        if (currentValue != expression.value) {
                            numberConditionsTrue++;
                        }
                    }
                    else {
                        if (currentValue == expression.value) {
                            numberConditionsTrue++;
                        }
                    }
                });

                // We matched all AND linked conditions of this array -> Set this pipe to the active color
                if (numberConditionsTrue === numberConditionsTotal) {
                    pipeAnimated = true;
                    var newColor = getPipeColor(pipe.referencePipe);
                    if (newColor !== defaultColor) {  // Only animate if the referenced pipe is not default aka. not empty
                        pipe.pipes.forEach(function (relatedPipe) {
                            setPipeColor(relatedPipe, newColor);
                        });
                    }
                }
            });
            // None of the expressions of this rule is true -> Set the default color
            if (!pipeAnimated) {
                /*
                    Update: Do Nothing
                */


                // Set color of pipes to default
                // pipe.pipes.forEach(function (relatedPipe) {
                //     setPipeColor(relatedPipe, pipe.defaultColor);
                // });
            }
        }
        checkPipeList();
    }

    /*
        Gets the element state for each type (like pump, airflow, valve,...).
        Returns null if something is wrong (e.g. element not found, wrongly configured, ...)
    */
    function getElementState(element) {
        var tag = "";
        var onTag = "";
        try {
            switch (element.type) {
                case "agitator": {
                    tag = processTags["Agitator[" + element.number + "].hmiIn.actValue"];
                    onTag = processTags["Agitator[" + element.number + "].hmiIn.on"];
                    adviseNewTag(tag);
                    adviseNewTag(onTag);
                    var val = GetTagValueAsDouble(tag);
                    var on = GetTagValueAsInteger(onTag);
                    on = Math.abs(on);
                    if (element.threshold) {
                        if (val > element.threshold) return on;
                    }
                    else {
                        return on;
                    }
                    return null;
                }

                case "analog": {
                    tag = processTags["AnalogSensor[" + element.number + "].hmiIn.actValue"];
                    adviseNewTag(tag);
                    var val = GetTagValueAsDouble(tag);
                    if (element.threshold) {
                        if (val > element.threshold) return 1;
                        else return 0;
                    }
                    else {
                        if (val > 0) return 1;
                        else return 0;
                    }
                    return null;
                }

                case "pump": {
                    tag = processTags["Pump[" + element.number + "].hmiIn.actValue"];
                    onTag = processTags["Pump[" + element.number + "].hmiIn.on"];
                    adviseNewTag(tag);
                    adviseNewTag(onTag);
                    var val = GetTagValueAsDouble(tag);
                    var on = GetTagValueAsInteger(onTag);
                    on = Math.abs(on);
                    if (element.threshold) {
                        if (val > element.threshold) return on;
                    }
                    else {
                        return on;
                    }
                    return null;
                }

                case "StrokeCounter": {
                    tag = processTags["StrokeCounter[" + element.number + "].hmiIn.actValue"];
                    onTag = processTags["StrokeCounter[" + element.number + "].hmiIn.on"];
                    adviseNewTag(tag);
                    adviseNewTag(onTag);
                    var val = GetTagValueAsDouble(tag);
                    var on = GetTagValueAsInteger(onTag);
                    on = Math.abs(on);
                    if (element.threshold) {
                        if (val > element.threshold) return on;
                    }
                    else {
                        return on;
                    }
                    return null;
                }

                case "sensor": {
                    tag = processTags["BinSensor[" + element.number + "].hmiIn.ActualState"];
                    adviseNewTag(tag);
                    var val = GetTagValueAsInteger(tag);
                    if (val != 0) return 1;
                    else return 0;
                }

                case "switch": {
                    tag = processTags["Switch[" + element.number + "].hmiIn.ActualState"];
                    adviseNewTag(tag);
                    var val = GetTagValueAsInteger(tag);
                    if (val != 0) return 1;
                    else return 0;
                }

                case "tank": {
                    tag = processTags["Tank[" + element.number + "].hmiIn.ActVolume"];
                    adviseNewTag(tag);
                    var val = GetTagValueAsDouble(tag);
                    if (element.threshold) {
                        if (val > element.threshold) return 1;
                        else return 0;
                    }
                    else {
                        if (val > 0) return 1;
                        else return 0;
                    }    
                    return null;
                }

                case "valve": {
                    tag = processTags["Valve[" + element.number + "].hmiIn.actValue"];
                    adviseNewTag(tag);
                    var val = GetTagValueAsInteger(tag);
                    if (val != 0) return 1;
                    else return 0;
                }

                case "pipe": {
                    if (pipes[element.name]) {
                        var linecol = pipes[element.name].getAttribute("stroke");
                        for (var colorKey in colors) {
                            if (linecol == colors[colorKey]) return colorKey;
                        }
                        throw "Color " + linecol + " of pipe " + element.name + " not configured.";
                    }
                    else {
                        var svgElement = svgContainer.querySelector("#" + element.name);
                        if (!svgElement) {
                            throw "Could not find pipe with id " + element.name;
                        }
                        pipes[element.name] = svgElement;
                        var linecol = svgElement.getAttribute("stroke");
                        for (var colorKey in colors) {
                            if (linecol.replace(" ", "") == colors[colorKey].replace(" ", "")) return colorKey;
                        }

                        throw "Color " + linecol + " of pipe " + element.name + " not configured.";
                    }
                }
            }
        }
        catch (e) {
            AptWriteLog("[APT Error] " + e, "warn");
            return null;
        }
    }

    /*
        Set defined color to the pipe. Pipe is selected by the ID attribute, which is the name from powerpoint.
        ToDo: Set color to be the real RGBA value, not some reference!! CONTINUE HERE ----------------
    */
    function setPipeColor(pipeId, color) {
        try {
            var element;

            if (pipes[pipeId]) {
                element = pipes[pipeId];
            }
            else {
                element = svgContainer.querySelector("#" + pipeId);
                if (!element) throw "Pipe with id " + pipeId + " cannot be found in SVG. Check for correct id";
                pipes[pipeId] = element;
            }

            var actualCode = element.getAttribute("stroke");
            if (actualCode !== color) {
                // console.log("Setting color " + color + " to pipeId " + pipeId);
                svgContainer.querySelector("#" + pipeId).setAttribute("stroke", color);
                svgContainer.querySelector("#" + pipeId).setAttribute("fill", color);
            }
            pipeList[pipeId] = true;

        }
        catch (e) {
            errorCount++;
            if (errorCount < 50) {
                AptWriteLog("[APT Warning] " + e, "warn");
            }
            if (errorCount > 5000) {
                clearInterval(loopingInterval);
            }
        }
    }

    function getPipeColor(pipeId) {
        var color = null;
        try {
            var element;

            if (pipes[pipeId]) {
                element = pipes[pipeId];
            }
            else {
                element = svgContainer.querySelector("#" + pipeId);
                if (!element) throw "Pipe with id " + pipeId + " cannot be found in SVG. Check for correct id";
                pipes[pipeId] = element;
            }

            color = element.getAttribute("stroke");
            if (color === "none") color = element.getAttribute("fill");
        }
        catch (e) {
            errorCount++;
            if (errorCount < 50) {
                AptWriteLog("[APT Warning] " + e, "warn");
            }
            if (errorCount > 5000) {
                clearInterval(loopingInterval);
            }
        }
        return color;
    }

    /*
        This function makes a list of all advised tags (like valves, pumps, ...).
        If any of the tags change all pipes are animated again.
        Tags are advised by advisedId, and are cleaned up when closing the window.
    */
    function adviseNewTag(tag) {
        if (tagList.indexOf(tag) !== -1) return;
        else {
            tagList.push(tag);
//            console.log("Advising new tag " + tag);
            emosWS.advise(tag, function (msg) {
                if (timeOutReached) {
                    checkPipes();

                    timeOutReached = false;
                    setTimeout(function () {
                        timeOutReached = true;
                        checkPipes();
                    }, debounceTime);
                }
            }).setId(advisedId);
        }

    }

    initPipe();
}

// Start script when svg is loaded
// plradfar: in some strange situations, event after load is not executed. Check getCurrentTime for SVG is remedium
/* 
document.getElementById("mainsvg").addEventListener('load', function () {
    console.log("Start pipe animation");
    animatePipes();
}, false);
*/

var svgReady = function () {
  var svgMain = document.getElementById('mainsvg');
  var svgDoc = svgMain.contentDocument;
  var svgRoot = svgDoc ? svgDoc.rootElement : null;

  if (svgRoot && svgRoot.getCurrentTime && (svgRoot.getCurrentTime() > 0))
  {
    console.log("Start pipe animation");
    animatePipes();
    return;
  }
  window.requestAnimationFrame(svgReady);
};
window.requestAnimationFrame(svgReady);
